<!--
Copyright 2013 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<!DOCTYPE html>
<style type="text/css">
video {
  width: 100px;
}
</style>

<!--
Videos are taken from
http://techslides.com/sample-webm-ogg-and-mp4-video-files-for-html5

We serve them from a remote host to make sure they are served with a 206
response code, to work around Chrome bug
https://code.google.com/p/chromium/issues/detail?id=121765.
-->
<div id="videos">
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
  <video preload="auto">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.webm" type="video/webm">
    <source src="http://web-animations.github.io/web-animations-js/test/testcases/small.mp4" type="video/mp4">
  </video>
</div>
  
<script src="../bootstrap.js" nochecks></script>
<script>
"use strict";

var dt = document.timeline;

// Most of these tests require the video to have loaded before they run.
// However, the test harness does not allow timing_test()s to be started after
// the page has finished loading. This means that we can't add the video
// dynamically then wait for it to load before testing.
//
// As a best-effort work-around, we include all videos in static HTML with
// preload="auto" to hint to the browser that they should be loaded
// immediately. However, this does not guarantee that they will be loaded when
// the page's load event fires, so the tests are flaky.
//
// TODO: Fix the test runner to allow the use of async_test() with
// timing_test(), which will allow the video elements to be created dynamically.
// See https://github.com/web-animations/web-animations-js/issues/321
var videos = document.getElementById("videos").getElementsByTagName('video');
// Cache this length, as videos is a live NodeList.
var numStaticVideos = videos.length;
var nextIndex = 0;
function getNextVideo() {
  if (nextIndex >= numStaticVideos) {
    throw new Error('No more videos!');
  }
  return videos[nextIndex++];
}

function createVideo() {
  // Videos taken from
  // http://techslides.com/sample-webm-ogg-and-mp4-video-files-for-html5
  var video = document.createElement("video");
  var webmSource = document.createElement("source");
  webmSource.setAttribute("src", "small.webm");
  webmSource.setAttribute("type", "video/webm");
  video.appendChild(webmSource);
  var mp4Source = document.createElement("source");
  mp4Source.setAttribute("src", "small.mp4");
  mp4Source.setAttribute("type", "video/mp4");
  video.appendChild(mp4Source);
  document.getElementById("videos").appendChild(video);
  return video;
}

function testOnceVideosLoaded(callback, message) {
  timing_test(function() {
    at(0.0, callback);
  }, message);
}

function createTestMediaReference(mediaElement, timing, parent) {
  return new MediaReference(mediaElement, timing, parent, 0.0);
}

var expectedVideoLength = 5.568;


// Test that MediaReference disables looping.
test(function() {
  var video = getNextVideo();
  video.loop = true;
  createTestMediaReference(video);
  assert_false(video.loop);
}, "MediaReference should disable looping");

// Test intrinsic iteration duration before media loads.
test(function() {
  assert_equals(createTestMediaReference(createVideo()).iterationDuration,
      Infinity);
}, "Intrinsic iterationDuration should be Infinity before media loads");

// Test intrinisc iteration duration.
testOnceVideosLoaded(function() {
  assert_approx_equals(
      createTestMediaReference(getNextVideo()).iterationDuration,
      expectedVideoLength, 0.001);
}, "Intrinsic iteration duration should be media duration");

// Test that iteration duration can be overridden.
testOnceVideosLoaded(function() {
  assert_equals(createTestMediaReference(getNextVideo(), 1.0).iterationDuration,
      1.0);
}, "Iteration duration should be overridable");

// Test basic use.
timing_test(function() {
  var video = getNextVideo();
  at(0.0, function() {
    dt.play(createTestMediaReference(video));
    assert_equals(video.playbackRate,  1.0);
    assert_false(video.paused);
  });
}, "Basic use");

// Test clipping where iterationDuration limits. Video should be frozen at end
// of iterationDuration.
timing_test(function() {
  var video = getNextVideo();
  at(0.0, function() {
    dt.play(createTestMediaReference(video, 3.0));
  });
  at(3.0, function() {
    assert_equals(video.currentTime, 3.0);
    assert_true(video.paused);
  });
  at(4.0, function() {
    assert_equals(video.currentTime, 3.0);
    assert_true(video.paused);
  });
}, "Video should be frozen at iterationDuration");

// Test clipping where video length limits. Video should be frozen at end for
// remainder of iterationDuration.
timing_test(function() {
  var video = getNextVideo();
  at(0.0, function() {
    dt.play(createTestMediaReference(video, video.duration + 2));
  });
  // We can't use video.duration here because the video isn't yet loaded.
  at(expectedVideoLength + 1, function() {
    assert_equals(video.currentTime, video.duration);
    assert_true(video.paused);
  });
  // We can't use video.duration here because the video isn't yet loaded.
  at(expectedVideoLength + 2, function() {
    assert_equals(video.currentTime, video.duration);
    assert_true(video.paused);
  });
}, "Video should be frozen at end");

// Test fill with iterationDuration limiting. Video should be frozen at end of
// iterationDuration.
timing_test(function() {
  var video = getNextVideo();
  at(0.0, function() {
    dt.play(createTestMediaReference(video, 1.0));
  });
  at(2.0, function() {
    assert_equals(video.currentTime,  1.0);
    assert_equals(video.paused,  true);
  });
}, "Should freeze video at iterationDuration for fill");

// Test fill with video length limiting. Video should be at end.
timing_test(function() {
  var video = getNextVideo();
  at(0.0, function() {
    dt.play(createTestMediaReference(video), video.duration + 1.0);
  });
  // We can't use video.duration here because the video isn't yet loaded.
  at(expectedVideoLength + 2, function() {
    assert_equals(video.currentTime,  video.duration);
  });
}, "Should freeze video at end for fill");

// Test no fill. Video should be frozen at start.
timing_test(function() {
  var video = getNextVideo();
  at(0.0, function() {
    dt.play(createTestMediaReference(video, {fillMode: "none"}));
  });
  // We can't use video.duration here because the video isn't yet loaded.
  at(expectedVideoLength + 1, function() {
    assert_equals(video.currentTime,  0);
    assert_equals(video.paused,  true);
  });
}, "Should freeze video at start for no fill");

// Test iterations.
timing_test(function() {
  var video = getNextVideo();
  at(0.0, function() {
    dt.play(createTestMediaReference(video,
        {iterationCount: 2.0, iterationDuration: 1.0}));
  });
  at(1.5, function() {
    assert_equals(video.currentTime,  0.5);
  });
}, "Video should respect iterationCount");

// Test iterationStart.
timing_test(function() {
  var video = getNextVideo();
  at(0.0, function() {
    dt.play(createTestMediaReference(video,
        {iterationStart: 0.5, iterationDuration: 2.0}));
  });
  at(0.5, function() {
    assert_equals(video.currentTime,  1.5);
  });
}, "Video should respect iterationStart");

// Test playback rate.
timing_test(function() {
  var video = getNextVideo();
  at(0.0, function() {
    dt.play(createTestMediaReference(video, {playbackRate: 2.0}));
  });
  at(1.0, function() {
    assert_equals(video.currentTime, 2.0);
    assert_equals(video.playbackRate, 2.0);
  });
}, "Video should respect playbackRate");

// Test heirachy of playback rates.
timing_test(function() {
  var video = getNextVideo();
  at(0.0, function() {
    dt.play(new ParGroup([createTestMediaReference(video, {playbackRate: 1.5})],
        {playbackRate: 2.5}));
  });
  at(1.0, function() {
    assert_equals(video.currentTime, 3.75);
    assert_equals(video.playbackRate, 3.75);
  });
}, "Video should respect playbackRate heirachy");

// Test interaction with MediaElement.defaultPlaybackRate.
timing_test(function() {
  var video = getNextVideo();
  at(0.0, function() {
    video.defaultPlaybackRate = 2.5;
    dt.play(createTestMediaReference(video, {playbackRate: 1.5}));
  });
  at(1.0, function() {
    assert_equals(video.currentTime, 3.75);
    assert_equals(video.playbackRate, 3.75);
  });
}, "Video should respect playbackRate after setting defaultPlaybackRate");

// Test reversing.
timing_test(function() {
  var video = getNextVideo();
  at(0.0, function() {
    dt.play(createTestMediaReference(video, {direction: "reverse"}));
  });
  at(1.0, function() {
    assert_approx_equals(video.currentTime, video.duration - 1.0, 0.0001);
    assert_equals(video.playbackRate, -1.0);
  });
}, "Video should respect reversing");

// Test negative playback rate.
timing_test(function() {
  var video = getNextVideo();
  at(0.0, function() {
    dt.play(createTestMediaReference(video, {playbackRate: -0.5}));
  });
  at(1.0, function() {
    assert_approx_equals(video.currentTime, video.duration - 0.5, 0.0001);
    assert_equals(video.playbackRate, -0.5);
  });
}, "Video should respect negative playbackRate");

// Test interaction of reversing and playback rate.
timing_test(function() {
  var video = getNextVideo();
  at(0.0, function() {
    dt.play(new ParGroup(
        [createTestMediaReference(video, {playbackRate: -0.5})],
        {playbackRate: 3.0, direction: "reverse"}));
  });
  at(1.0, function() {
    assert_equals(video.currentTime, 1.5);
    assert_equals(video.playbackRate, 1.5);
  });
}, "Video should respect reversing and playbackRate");

// Test zero iterationDuration.
timing_test(function() {
  var video = getNextVideo();
  at(0.0, function() {
    dt.play(createTestMediaReference(video, 0.0));
    assert_equals(video.currentTime,  0.0);
  });
  at(1.0, function() {
    assert_equals(video.currentTime,  0.0);
  });
}, "Video should start at time zero when iterationDuration is zero");

// Test zero intrinsic iterationDuration.
// TODO: Need to find a zero length video.

// Test that media elements are removed from their MediaController.
timing_test(function() {
  var video = getNextVideo();
  at(0.0, function() {
    video.controller = new MediaController();
    dt.play(createTestMediaReference(video));
    assert_equals(video.controller,  null);
  });
  // Seeking the video will throw if the controller has not been detached.
  at(1.0, function() {
    assert_equals(video.currentTime,  1.0);
  });
}, "Video should be detached from controller when used in MediaReference");

// Test limited seek ranges.
// TODO: Need to find a video with limited seek ranges.

// Test that video is paused when its MediaReference is detached from its
// Player.
timing_test(function() {
  var video = getNextVideo();
  var player;
  at(0.0, function() {
    player = dt.play(createTestMediaReference(video));
  });
  at(1.0, function() {
    assert_false(video.paused);
    player.source = null;
  });
  at(2.0, function() {
    assert_true(video.paused);
  });
}, "Video should be paused when MediaReference is detached from its Player");

</script>
